//
// Created by martin on 2/10/22.
//

#include "PollPoller.h"
#include "muduo/base/Logging.h"
#include "muduo/net/Channel.h"

#include <poll.h>

using namespace muduo;
using namespace muduo::net;

PollPoller::PollPoller(EventLoop *loop)
: Poller(loop)
{
}

PollPoller::~PollPoller() = default;

/*
 * Core function for PollPoller
 *
 * invoke poll(2) to get the number of ready fds.
 */
Timestamp PollPoller::poll(int timeoutMs, Poller::ChannelList *activeChannels)
{
    int numEvents = ::poll(&*pollfds_.begin(), pollfds_.size(), timeoutMs);
//    int numEvents = ::poll(pollfds_.data(), pollfds_.fileSize(), timeoutMs); // Okay too

    int savedErrno = errno;
    Timestamp now(Timestamp::now());
    if (numEvents > 0)
    {
        LOG_TRACE << numEvents << " nothing happened";
        fillActiveChannels(numEvents, activeChannels); // find out fds with active event
    }
    else if (numEvents == 0)
    {
        LOG_TRACE << " nothing happened";
    }
    else
    {
        if (savedErrno != EINTR) {
            errno = savedErrno;
            LOG_SYSERR << "PollPoller::poll()";
        }
    }
    return now;
}

/**
 * Traverse pollfds_ to find out all fds with active event, then fill in the activeChannels
 *
 * O(N)
 *
 * @param numEvents number of event
 * @param activeChannels [out]  active channels with active event
 */
void PollPoller::fillActiveChannels(int numEvents, Poller::ChannelList *activeChannels) const
{
    // traverse vector pollfds_, and find out the channel in map channels_ by fd,
    // then push back the channel to the activeChannels
    for (PollFdList::const_iterator pfd = pollfds_.begin();
    pfd != pollfds_.end() && numEvents > 0; ++pfd)
    {
        if (pfd->revents > 0)
        {
            --numEvents;
            // find pfd->fd in map channels_
            ChannelMap::const_iterator ch = channels_.find(pfd->fd);
            assert(ch != channels_.end());
            Channel* channel = ch->second;
            assert(channel->fd() == pfd->fd);
            // current active event saved to channel for Channel::handleEvent()
            channel->set_revents(pfd->revents);
            // pfd->revents = 0;
            activeChannels->push_back(channel);
        }
    }
}

/**
 * Update array pollfds_
 *
 * O(logN)
 */
void PollPoller::updateChannel(Channel *channel)
{
    Poller::assertInLoopThread();
    LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
    if (channel->index() < 0)
    { // a new one, add to pollfds_
        // ensure channel point to a new one
        assert(channels_.find(channel->fd()) == channels_.end());
        struct pollfd pfd;
        pfd.fd = channel->fd();
        pfd.events = static_cast<short>(channel->events());
        pfd.revents = 0;
        pollfds_.push_back(pfd);
        int idx = static_cast<int>(pollfds_.size()) - 1;
        channel->set_index(idx);
        channels_[pfd.fd] = channel; // insert (fd, channel)
    }
    else
    { // update existing one
        assert(channels_.find(channel->fd()) != channels_.end());
        assert(channels_[channel->fd()] == channel);
        int idx = channel->index();
        // ensure channel does exist in pollfds_
        assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
        struct pollfd& pfd = pollfds_[idx];
        assert(pfd.fd == channel->fd() || pfd.fd == -channel->fd() - 1);
        pfd.fd = channel->fd();
        pfd.events = static_cast<short>(channel->events());
        pfd.revents = 0;
        if (channel->isNoneEvent())
        {
            // ignore this pollfd
            // 暂时忽略该文件描述符的事件
            // 这里pfd.fd 可以直接设置为-1. 为什么减去1?　因为有一个特殊fd值0.
            pfd.fd = -channel->fd() - 1; // 这样设置为了removeChannel优化
        }
    }
}

/**
 * 从监听的通道数组channels_中, 移除指定通道
 * @note removeChannel指定通道之前, 一定要先调用updateChannel(), 要把监听的事件设置为不关注事件(NoneEvent)
 */
void PollPoller::removeChannel(Channel *channel)
{
    Poller::assertInLoopThread();
    LOG_TRACE << "fd = " << channel->fd();
    assert(channels_.find(channel->fd()) != channels_.end());
    assert(channels_[channel->fd()] == channel);
    assert(channel->isNoneEvent());
    int idx = channel->index();
    assert(0 <= idx && idx < static_cast<int>(pollfds_.size()));
    const struct pollfd& pfd = pollfds_[idx]; (void)pfd;

    // ensure remove one invalid channel from channels_
    assert(pfd.fd == -channel->fd() - 1 && pfd.events == channel->events());
    size_t n = channels_.erase(channel->fd()); // erase key, 只要存在, 返回值一定等于１
    assert(n == 1); (void)n;

    // remove pollfd from pollfds_ by index
    if (implicit_cast<size_t>(idx) == pollfds_.size() - 1) // pollfd_数组中最后一个pollfd
    { // last of pollfds_
        pollfds_.pop_back();
    }
    else
    { // 不是最后一个
        // swap the pollfd to be removed with the last of pollfds_,
        // then remove the last
        // 这里移除算法时间复杂度O(1), 将待删除的元素与最后一个元素交换后再pop_back
        int channelAtEnd = pollfds_.back().fd; // last fd in vector pollfds_
        iter_swap(pollfds_.begin() + idx, pollfds_.end() - 1);
        // <=> std::swap(pollfds_[idx], pollfds_[pollfds_.size() - 1]);
        if (channelAtEnd < 0)
        {
            channelAtEnd = -channelAtEnd - 1;
        }
        channels_[channelAtEnd]->set_index(idx);
        pollfds_.pop_back();
    }
}
